#define _GNU_SOURCE
#include "app_bt.h"
#include "log/log.h"
#include <string.h>
#include <unistd.h>
#include <string.h>
#include "app_serial.h"

static int init_bt(Device *device)
{
    // 初始化串口
    app_serial_init(device);


    // 将串口设置为非阻塞模式,并刷新生效 =》 防止蓝牙不用，status判断会阻塞
    app_serial_setBlock(device, 0);
    tcflush(device->fd, TCIOFLUSH);

    // 当前串口波特率9600
    // 当前蓝牙波特率可能9600/115200

    // 如果测试蓝牙可用，那蓝牙当前是9600，需要将蓝牙改为115200，重启蓝牙，sleep 2秒
    if (app_bt_status(device) == 0)
    {
        app_bt_setBraudRate(device, BT_BR_115200);
        app_bt_reset(device);
        sleep(2); // 等待重启完成
    }

    // 将串口波特率改为115200
    app_serial_setBraudRate(device, BR_115200);
    tcflush(device->fd, TCIOFLUSH);
    // 确认蓝牙是否可用，如果不可用，失败返回
    if (app_bt_status(device) != 0)
    {
        log_error("蓝牙初始化失败");
        return -1;
    }
    // 设置蓝牙的其它属性
    app_bt_rename(device, "atguigu");
    app_bt_setNetid(device, "1234"); // 每个组不同
    app_bt_setMaddr(device, "0101"); // 组内的不同

    // 将串口设置为阻塞模式,并刷新生效
    app_serial_setBlock(device, 1);
    int result = tcflush(device->fd, TCIOFLUSH);
    if (result != 0)
    {
        log_error("蓝牙初始化失败");
        return -1;
    }
    log_debug("蓝牙初始化成功");

    

    return 0;
}

/**
 * 初始化蓝牙
 */
int app_bt_init(Device * device)
{
    // 给设备指定pre_write和post_read函数属性
    device->pre_write = app_bt_preWrite;
    device->post_read = app_bt_postRead;

    init_bt(device);
    
    return 0;
}


/**
 * 蓝牙数据的写前处理
    字符数组消息: 
        例子：1 2 3 XX abc
        格式：conn_type id_len msg_len id msg
    蓝牙发送数据格式例子：
        例子：41 54 2b 4d 45 53 48 00 ff ff 61 62 63 0d 0a
        41 54 2b 4d 45 53 48 00： AT+MESH（固定头部）
        ff ff: 对端的MADDR（如果是FFFF代表群发）
        61 62 63: 要发送的数据（不超过12字节）
        0d 0a：\r\n（固定结尾）
 */
int app_bt_preWrite(char *data, int len)
{
    // 检查数据长度不能小于6
    if (len < 6)
    {
        log_error("字符数组消息数据长度不能小于6");
        return -1;
    }

    // 从data中取出conn_type/id_len/msg_len/id/msg
    int conn_type = data[0];
    if (conn_type!=1)
    {
        log_error("连接类型错误， 应该是1");
        return -1;
    }
    int msg_len = data[2];
    char id[2];
    memcpy(id, data+3, 2);
    char msg[msg_len];
    memcpy(msg, data+5, msg_len);

    // 清除data数据
    memset(data, 0, len);

    // 向data中写入AT指令数据
    // 41 54 2b 4d 45 53 48 00 ff ff 61 62 63 0d 0a
    memcpy(data, "AT+MESH", 8); // 固定头部  8
    memcpy(data + 8, id, 2); // id 2
    memcpy(data + 10, msg, msg_len); // msg
    memcpy(data + 10 + msg_len, "\r\n", 2); // 固定结尾 2

    return 8 + 2 + msg_len + 2;
}

static char read_buf[1024]; // 缓存蓝牙数据的容器
static int data_len; // 缓存蓝牙数据的长度
static char fix_heads[2] = {0xf1,0xdd}; // 固定头部

// 删除read_buf中前面指定长度的无用数据
void remove_data(int len)
{
    memmove(read_buf, read_buf+len, data_len-len);
    data_len -= len;
}

/**
 * 蓝牙数据的读后处理
- 从蓝牙读取到数据后的处理 postRead
    接收方得到数据2（3 + [2]）：f1 dd 07 23 23 ff ff 41 42 43
        f1 dd : 固定的头部
        07： 之后数据的长度（5-16之间） 
        23 23：对端（发送方）的MADDR
        ff ff: 我的MADDR或ffff(群发)
        41 42 43：发送的数据 
    处理后的数据格式：conn_type id_len msg_len id msg

    拼接接收到的数据，缓存一些来，将缓存中无用的数据删除，找出一块完整的蓝牙数据 =》 生成字符数组消息
 */
int app_bt_postRead(char *data, int len)
{
    // 将data添加到缓存中
    memcpy(read_buf+data_len, data, len);
    data_len += len;

    // 如果蓝牙数据长度小于8，则直接返回
    if (data_len < 8)
    {
        return 0;
    }

    // 遍历蓝牙数据data， 找出一块完整的蓝牙数据，删除无用数据
    int i;
    for (i = 0; i < data_len-7; i++)
    {   // f1 dd 07 23 23 ff ff 41 42 43
        // 如果前二位是{0xf1,0xdd},就是需要的蓝牙数据  compare
        if(memcmp(read_buf+i, fix_heads, 2) == 0)
        {
            // 删除前面无用的
            if (i>0)
            {
                remove_data(i);
                // 再次检查数据长度
                if (data_len < 8)
                {
                    return 0;
                }
            }

            // 通过长度来检查是否有完整的蓝牙数据
            int blue_len = 3 + read_buf[2];
            if (data_len < blue_len)
            {
                log_debug("读取蓝牙数据不完整，需要继续读取");
                return 0;
            }

            // 清除data
            memset(data, 0, len);
            // 根据read_buf中的蓝牙数据生成字符数组消息写入data
            // conn_type id_len msg_len id msg
            // f1 dd 07 23 23 ff ff 41 42 43
            data[0] = 1; // 蓝牙连接
            data[1] = 2; // id长度
            data[2] = blue_len - 7; // msg长度
            memcpy(data + 3, read_buf + 3, 2); // id
            memcpy(data + 5, read_buf +7, data[2]); // msg

            // 删除已经处理过的蓝牙数据
            remove_data(blue_len);
            
            // 返回字符数组消息的长度
            return 3 + 2 + data[2];
        }
    }
    // 如果没有找到蓝牙数据，删除所有遍历过的蓝牙数据
    if (i> 0)
    {
        remove_data(i);
    }
    
    return 0;
}

static int wait_ack(Device *device)
{
    // 等待一定的时间 50ms
    usleep(50 * 1000);
    // 读取串口中的数据
    char data_buf[4];
    read(device->fd, data_buf, sizeof(data_buf));
    // 如果是“OK\r\n”, 说明蓝牙可用返回0, 否则不可用返回-1
    if (memcmp(data_buf, "OK\r\n", 4) != 0)
    {
        log_debug("测试蓝牙可用没通过");
        return -1;
    }
    return 0;
}

/**
 * 判断蓝牙是否连接
 */
int app_bt_status(Device *device)
{
    // 向串口中写入AT指令
    write(device->fd, "AT\r\n", 4);
    
    return wait_ack(device);
}

/**
 * 蓝牙重命名
 */
int app_bt_rename(Device *device, char *name)
{
    char cmd[20];
    sprintf(cmd, "AT+NAME%s\r\n", name);
    // 向串口中写入AT+NAMExxx指令
    write(device->fd, cmd, strlen(cmd));

    return wait_ack(device);
}

/**
 * 设置蓝牙波特率
 */
int app_bt_setBraudRate(Device *device, BTBraudRate baudrate)
{
    // 拼接命令
    char cmd[20];
    sprintf(cmd, "AT+BAUD%c\r\n", baudrate);

    // 向串口中写入AT+NAMExxx指令
    write(device->fd, cmd, strlen(cmd));

    return wait_ack(device);
}

/**
 * 重启蓝牙
 */
int app_bt_reset(Device *device)
{
    // 向串口中写入AT指令
    write(device->fd, "AT+RESET\r\n", 10);
    
    return wait_ack(device);
}

/**
 * 设置蓝牙网络ID
 * netid: 四位的16进制字符串  “0101”
 */
int app_bt_setNetid(Device *device, char *netid)
{
    // 拼接命令
    char cmd[20];
    sprintf(cmd, "AT+NETID%s\r\n", netid);

    // 向串口中写入AT+NETIDxxxx指令
    write(device->fd, cmd, strlen(cmd));

    return wait_ack(device);
}

/**
 * 设置蓝牙MAC地址
 * maddr: 四位的16进制字符串  “a1a2”
 */
int app_bt_setMaddr(Device *device, char *maddr)
{
    // 拼接命令
    char cmd[20];
    sprintf(cmd, "AT+MADDR%s\r\n", maddr);

    // 向串口中写入AT+MADDRxxxx指令
    write(device->fd, cmd, strlen(cmd));

    return wait_ack(device);
}

